#include "edulisp_analyser.hpp"

#include <iostream>

#define OP_MAX	2
//int GetTypeOf(Lexeme L, NameTable& vNT)
//{
//	if(L > 0)
//		return OP_MAX + (vNT[L].GetLexemeType() == LT_ID) ? 1 : 2;
//	else
//		return -L;
//}

static int GetTypeOf(Lexeme L) // не должна возвращать 0
{
	// идентификатор - не отсеченный на уровне сканера
	if (L >= 0) return LC_ID;

	// обработка каждого параметра в отдельности - во избежание
	switch (-L)
	{
		// биндинг к реалиям анализатора - надо придумать связь получше
		case SSMO_BRACKET: return LC_BRACK;
		case SSMO_BRACKET_CLOSING: return LC_CBRACK;

		default: return LC_ID; // непредусмотренное пытается пробежать
						       // под видом идентификатора
	}
}

int Analyse(LexemeList& lLexemeList, NameTable& vNameTable)
{
	stack<Head> LS;

	//Head Empty;
	//Empty.push_back(SC_NULL);
	//LS.push(Empty);

	LS.push(HC_NULL); // можно будет заменить на стуктуру, в которой хранятся
	                  // значения параметров и отдельное поле под тип,
	                  // иначе GetTypeOf

	PhraseCategory U = PC_NULL; // категория последней разобранной фразы

	// Раздать имена этой хренотени
	// Вынести за пределы анализатора вместе с матрицей
	// Матрицу передавать в качестве параметра?
	enum { E1 = 1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15,
	       E16, E17, E18, E19, E20, E21, E22, E23, E24, E25, E26, E27, E28, E29,
	       E30, E31, E32, E33, E34, E35, E36, E37, E38, E39, E40, E41, E42, E43,
	       E44, E45, E46, E47, E48, E49, E50, E51, E52, E53, E54, E55, E56, E57,
	       E58, E59, E60, E61, E62, E63, E64, E65, E66, E67, E68, E69, E70, E71,
	       E72, E73, E74, E75, E76, E77, E78, E79, E80 };

	const int TransitionMatrix[][LC_GOTO + 1] = {
	//  NULL   BRCK CBRK DFN  LMBD IF   ID   LABEL <<< Важен порядок
		{ E1,  E2,  E3,  E4,  E5,  E6,  E7,  E71 }, // #: HC_NULL
		{ E8,  E9,  E10, E11, E12, E13, E14, E72 }, // (: HC_BRACKET
		{ E15, E16, E17, E18, E19, E20, E21, E73 }, // (<list>): HC_LIST
		{ E22, E23, E24, E25, E26, E27, E28, E74 }, // i: HC_ID
		{ E29, E30, E31, E32, E33, E34, E35, E75 }, // (defun
		{ E36, E37, E38, E39, E40, E41, E42, E76 }, // (defun (<nm>/<arg>)
		{ E43, E44, E45, E46, E47, E48, E49, E77 }, // lambda: HC_LAMBDA
		{ E50, E51, E52, E53, E54, E55, E56, E78 }, // (if: HC_IF
		{ E57, E58, E59, E60, E61, E62, E63, E79 }, // (if <atom> (: HC_IF_T
		{ E64, E65, E66, E67, E68, E69, E70, E80 }  // (if <a> () (: HC_IF_N
	};                                                // ^^^ Тут тоже

//	Head *Top; // память должна освобождаться при выполнении
	           // только для структуры, если она понадобится

	std::cout << endl << "Analyse progress:" << endl;

	LexemeList::const_iterator R = lLexemeList.begin();
	while (R != lLexemeList.end())
	{
		// Отладочный вывод
//		std::cout << "[" << LS.top() << ":";
//		std::cout << GetTypeOf(*R) << "/" << *R << "] = E";
//		std::cout << TransitionMatrix[LS.top()][GetTypeOf(*R)];
//		std::cout << " U = " << U << endl;

		switch (TransitionMatrix[LS.top()][GetTypeOf(*R)])
		{

		case E2:
			if (U != PC_LIST && U != PC_NULL) return E2;
			LS.push(HC_BRACKET);
			U = PC_NULL;

			break;

		case E3: return E3; // закрывающая скобка из ничего

		case E4: // defun пришел
			if (U != PC_NULL) return E4;
			LS.push(HC_DEFUN);

			break;

		case E5: return E5; // lambda вне скобок, ага
		case E6: // здесь могла бы быть ваша адресация
			//LS.push(HC_IF);
			//U = PC_NULL;
			LS.push(HC_BRACKET);
			U = PC_ATOM;

			break;

		case E7: return E7; // идентификатор вне списка

		case E9:
			if (U == PC_NULL) return E9; // список начинается со списка
			LS.push(HC_BRACKET);
			U = PC_NULL;

			break;

		case E10:
			if (U != PC_ATOM && U != PC_LIST) return E10;
			// список не должен быть пустым

			LS.pop();
			LS.push(HC_LIST); // HC_LIST, в общем-то, но я запутался
			U = PC_NULL;

			break;

		case E11: return E11; // defun в скобках? сомнительно

		case E12: // lambda внутри скобки
			LS.push(HC_LAMBDA);
			U = PC_NULL;

			break;

		case E13: // обработка if - он объединяется со скобкой
			if (U == PC_NULL) return E13;
//			LS.push(HC_IF); // заменить на HC_IF
//			U = PC_NULL;
			LS.push(HC_BRACKET);
			U = PC_ATOM;

			break;

		case E14:
			//LS.push(HC_ID);
			U = PC_ATOM;

			break;

		case E15:
			LS.pop();
			R--; // проверяем предыдущую голову в стеке

			break;

		case E16:
			//if (U != PC_NULL) return E10; // болтающиеся атомы
			LS.push(HC_BRACKET);
			U = PC_NULL;

			break;

		case E17:
			U = PC_LIST;
			LS.pop();
			R--; // проверяем предыдущую голову (с учетом нового U)

			break;

		case E18: // defun после списка
			if (U != PC_NULL) return E18;
			LS.push(HC_DEFUN);

			break;

		case E19: return E19; // lambda без скобок

		case E20: // if после списка
//			LS.push(HC_IF);
//			U = PC_NULL;
			LS.push(HC_BRACKET);
			U = PC_ATOM;

			break;

		case E21:
			U = PC_ATOM;

			break;

		case E24:
			//if (U) return E15;
			LS.pop();
			LS.push(HC_LIST);
			U = PC_LIST;

			break;

		case E25: break; // nope

		case E31: // defun, терминал - независимо от наличия аргументов
			if (U == PC_LIST) return E31;
			LS.pop();
			LS.push(HC_DEFUNARG);
			U = PC_NULL;

			break;

		case E32: return E32; // defun в аргументах defun'а
		case E30: return E30; // открывающая скобка до терминала
		case E33: return E33; // lambda внутри defun

		case E34: return E34; // if в аргументах функции
		case E35: // аргумент функции
			U = PC_ATOM;

			break;

		//case E37:

		case E37: // открывающая скобка перед чудесами
			LS.pop();            // убираем defun
			LS.push(HC_BRACKET); // скобка за defun
			LS.push(HC_BRACKET);
			U = PC_NULL;

			break;

		case E38: // закрывающая скобка от defun
			      // не должна появляться, т.к. defun был заменен на скобку
			return E38;

//			U = PC_LIST; // должно подойти
//			LS.pop();
//			R--; // проверяем предыдущую голову (с учетом нового U)
//
//			break;

		case E39: return E39;
		case E40: return E40; // lambda внутри defun с аргументами

		case E41:
			if (U == PC_ATOM) return E41;
//			LS.push(HC_IF);
//			U = PC_NULL;
			LS.push(HC_BRACKET);
			U = PC_ATOM;

			break;

		case E42: return E42; // выполнение чудес до открывающей скобки
		case E44: return E44; // открывающая скобка до параметров

		case E45: // терминал для lambda
			if (U == PC_LIST) return E45;
			LS.pop(); // сверху должен быть bracket
			U = PC_ATOM;

			break;

		case E46: return E46; // defun внутри lambda
		case E47: return E47; // lambda внутри lambda

		case E48: return E48; // if в параметрах lambda

		case E49:
			U = PC_ATOM;

			break;

		case E51: // открывающая скобка после if
			if (U == PC_ATOM || U == PC_LIST)
			{
				LS.pop();
				LS.push(HC_IF_T);
				LS.push(HC_BRACKET);
			}
			else
			{
				LS.push(HC_BRACKET);
			}
			U = PC_NULL;

			break;

		case E52: return E52; // закрывающая скобка не обрабатывается в if
		case E53: return E53; // defun в if

		case E54: break; // пока непонятно, что с лямбдой

		case E55: return E55; // !!! до организации вложенного if !!!

		case E56:
			U = PC_ATOM;
			break;

		//------- IF <expr> ( -------
		case E58:
			if (U != PC_LIST) return E58; // но этой ошибки возникать не должно
			LS.pop();
			LS.push(HC_IF_N);
			LS.push(HC_BRACKET);
			U = PC_NULL;

			break;

		case E59: return E59; // закрывающая скобка должна была обработаться
		                      // в ветке HC_BRACKET

		case E60: return E60; // defun у ворот
		case E61: return E61; // lambda

		case E62: return E62; // до реализации вложенного If

		case E63: return E63; // id между выражениями

		//-------- IF <expr> ( <expr> ) ( ---------
		case E65: return E65; // начало третьего выражения
		case E66:
			if (U != PC_LIST) return E66;
			LS.pop();
			--R;

			break;

		default:
			// обработка внутрипрограммных ошибок
			break;
		}

		++R;
	}

	// двум концам списка не бывать
	switch (TransitionMatrix[LS.top()][0])
	{
		case E1:
			//if (U != PC_NULL || U != PC_LIST || U != PC_PROG) return E1;
			if (U == PC_ATOM) return E1; // сообщения об ошибках в russian.hpp?
			else;                        // успешное завершение (отдельная
									     // константа?)
			break;

		case E8: return E8;   // незакрытая скобка
		case E22: return E22; // идентификатор в беде
		case E29: return E29; // defun в беде
		case E36: return E36; // defun в беде, но с аргументами
		case E43: return E43; // неоконченная lambda

		default:
			// программная ошибка
			break;
	}

	return 0;
}
